# zk_route.py
from flask import render_template, request, jsonify, session, Blueprint, redirect, url_for, flash
from datetime import datetime, timedelta
import json
from flask import current_app
import time
import threading
from inc.models import db, ZktimeDevice, ZktimeAccessLog, AdminMember
from flask_login import login_required
from .zk_service import ZKDevice, ZKDeviceManager
from apscheduler.schedulers.background import BackgroundScheduler
from sqlalchemy import or_
import pythoncom
import win32com.client
import socket
from queue import Queue
import logging

# 设置日志
logger = logging.getLogger(__name__)

# 创建后台管理蓝图
zktime_bp = Blueprint('zktime', __name__, url_prefix='/zktime')

# 设备管理器实例
device_manager = ZKDeviceManager()

# 全局设备连接管理
device_connections = {}
device_connection_lock = threading.Lock()
device_management_queue = Queue()


# 设备状态枚举类
class DeviceStatus:
    OFFLINE = 0
    ONLINE = 1
    FAULT = 2


# 后台管理 - 设备列表
@zktime_bp.route('/devices', methods=['GET'])
@login_required
def device_list():
    devices = ZktimeDevice.get_all()

    # 检查设备状态
    for device in devices:
        if device.last_heartbeat:
            time_diff = (datetime.now() - device.last_heartbeat).total_seconds()
            if time_diff > device.offline_threshold:
                device.status = DeviceStatus.OFFLINE
                db.session.commit()

    # 获取所有用户数据
    all_users = AdminMember.query.filter_by(status=True).all()

    return render_template('admin/zktime/zk_devices.html', devices=devices, all_users=all_users)


@zktime_bp.route('/check_network', methods=['GET'])
@zktime_bp.route('/check_network/<ip_address>', methods=['GET'])
@zktime_bp.route('/check_network/<ip_address>/<int:port>', methods=['GET'])
@login_required
def check_network(ip_address=None, port=None):
    """检查网络连通性
    Args:
        ip_address: 设备IP地址
        port: 设备端口号
    """
    # 1. 处理IP：优先用路径参数，否则用查询参数
    ip = ip_address if ip_address is not None else request.args.get('ip')

    # 2. 处理Port：优先用路径参数（已转为int），否则用查询参数（转换+默认值）
    if port is None:
        port = request.args.get('port', default=4370, type=int)
    # 路径参数的port已经是int，无需额外处理

    # 3. 校验必填参数
    if not ip:
        return jsonify({'success': False, 'message': 'IP地址不能为空'}), 400

    # 4. 网络检查逻辑（与原代码一致）

    try:
        # 使用socket检查网络连通性
        with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
            sock.settimeout(3)
            result = sock.connect_ex((ip, port))

            if result == 0:
                return jsonify({
                    'success': True,
                    'message': f'网络连接正常: {ip}:{port}'
                })
            else:
                return jsonify({
                    'success': False,
                    'message': f'网络连接失败: {ip}:{port} (错误码: {result})'
                })

    except Exception as e:
        return jsonify({
            'success': False,
            'message': f'网络检查异常: {str(e)}'
        })


# 后台管理 - 添加设备
@zktime_bp.route('/device/add', methods=['GET', 'POST'])
@login_required
def device_add():
    if request.method == 'POST':
        name = request.form.get('name')
        ip_address = request.form.get('ip_address')
        port = request.form.get('port', 4370)
        protocol = request.form.get('protocol', 'TCP')
        password = request.form.get('password')
        description = request.form.get('description')

        # 验证必填字段
        if not all([name, ip_address]):
            flash('名称和IP地址不能为空', 'error')
            return render_template('admin/zktime/zk_devices_add.html', device=ZktimeDevice())

        # 检查网络连通性
        if not check_network(ip_address, int(port)):
            flash('无法连接到设备，请检查网络和端口', 'error')
            return render_template('admin/zktime/zk_devices_add.html', device=ZktimeDevice())

        try:
            device = ZktimeDevice(
                name=name,
                ip_address=ip_address,
                port=port,
                protocol=protocol,
                password=password,
                description=description,
                status=DeviceStatus.OFFLINE  # 显式设置初始状态为离线
            )
            db.session.add(device)
            db.session.commit()
            flash('设备添加成功', 'success')
            return redirect(url_for('zktime.device_list'))
        except Exception as e:
            db.session.rollback()
            flash(f'保存失败: {str(e)}', 'error')
            return render_template('admin/zktime/zk_devices_add.html', device=ZktimeDevice())

    return render_template('admin/zktime/zk_devices_add.html',  device=ZktimeDevice())


# 后台管理 - 编辑设备
@zktime_bp.route('/device/<int:device_id>/edit', methods=['GET', 'POST'])
@login_required
def device_edit(device_id):
    device = ZktimeDevice.get_by_id(device_id)
    if not device:
        flash('设备不存在', 'error')
        return redirect(url_for('zktime.device_list'))

    if request.method == 'POST':
        # 验证必填字段
        if not all([request.form.get('name'), request.form.get('ip_address')]):
            flash('名称和IP地址不能为空', 'error')
            return render_template('admin/zktime/zk_devices_edit.html', device=device)

        # 检查网络连通性
        new_ip = request.form.get('ip_address')
        new_port = request.form.get('port', 4370)
        if not check_network(new_ip, int(new_port)):
            flash('无法连接到设备，请检查网络和端口', 'error')
            return render_template('admin/zktime/zk_devices_edit.html', device=device)

        try:
            device.name = request.form.get('name')
            device.ip_address = new_ip
            device.port = new_port
            device.protocol = request.form.get('protocol', 'TCP')
            device.password = request.form.get('password')
            device.description = request.form.get('description')
            device.updated_at = datetime.now()
            device.status = DeviceStatus.OFFLINE  # 编辑时重置状态为离线

            db.session.commit()
            flash('设备更新成功', 'success')
            return redirect(url_for('zktime.device_list'))
        except Exception as e:
            db.session.rollback()
            flash(f'更新失败: {str(e)}', 'error')
            return render_template('admin/zktime/zk_devices_edit.html', device=device)

    return render_template('admin/zktime/zk_devices_edit.html', device=device)


# 后台管理 - 删除设备
@zktime_bp.route('/device/<int:device_id>/delete', methods=['POST'])
@login_required
def device_delete(device_id):
    device = ZktimeDevice.get_by_id(device_id)
    if not device:
        flash('设备不存在', 'error')
        return redirect(url_for('zktime.device_list'))

    # 释放设备连接
    device_manager.release_device(device_id)

    device.delete()
    flash('设备删除成功', 'success')
    return redirect(url_for('zktime.device_list'))


# 在应用启动时调用定时任务
def init_app(app):
    """初始化应用"""
    with app.app_context():
        start_scheduler()
        print("门禁设备状态监控定时任务已启动")


# 修改设备状态检查函数
def check_device_status():
    """定时检查设备状态"""
    with current_app.app_context():
        devices = ZktimeDevice.get_all()
        for device in devices:
            try:
                print(f"检查设备状态: {device.name} ({device.ip_address})")

                # 使用改进的连接测试
                zk_device = ZKDevice(
                    device.ip_address,
                    device.port,
                    device.protocol,
                    device.password
                )

                if zk_device.connect():
                    # 进一步验证设备是否真正响应
                    try:
                        # 尝试获取设备时间等简单操作来验证设备响应
                        if zk_device.test_connection()[0]:
                            device.status = DeviceStatus.ONLINE
                            device.last_heartbeat = datetime.now()
                            device.retry_count = 0
                            print(f"设备 {device.name} 在线且响应正常")
                        else:
                            device.status = DeviceStatus.FAULT
                            device.retry_count += 1
                            print(f"设备 {device.name} 连接但无响应")
                    except Exception as e:
                        device.status = DeviceStatus.FAULT
                        device.retry_count += 1
                        print(f"设备 {device.name} 验证响应失败: {e}")

                    zk_device.disconnect()
                else:
                    device.retry_count += 1
                    if device.retry_count >= 3:
                        device.status = DeviceStatus.FAULT
                    else:
                        device.status = DeviceStatus.OFFLINE
                    print(f"设备 {device.name} 离线，重试次数: {device.retry_count}")

                db.session.commit()

            except Exception as e:
                print(f"检查设备 {device.name} 状态失败: {e}")
                device.retry_count += 1
                if device.retry_count >= 3:
                    device.status = DeviceStatus.FAULT
                else:
                    device.status = DeviceStatus.OFFLINE
                db.session.commit()


# 修改测试连接函数，确保状态更新
@zktime_bp.route('/device/<int:device_id>/test', methods=['POST', 'GET'])
@login_required
def device_test(device_id):
    """测试设备连接"""
    device = ZktimeDevice.get_by_id(device_id)
    if not device:
        return jsonify({'success': False, 'message': '设备不存在'})

    try:
        zk_device = ZKDevice(
            device.ip_address,
            device.port,
            device.protocol,
            device.password
        )

        # 测试连接
        if zk_device.connect():
            # 进一步验证设备是否真正可用
            try:
                success, message = zk_device.test_connection()
                zk_device.disconnect()

                if success:
                    # 更新设备状态
                    device.status = DeviceStatus.ONLINE
                    device.last_heartbeat = datetime.now()
                    device.retry_count = 0
                    db.session.commit()

                    # 记录成功日志
                    log_device_action(
                        device_id=device_id,
                        action='test_connection',
                        result='success',
                        user_id=session.get('user_id'),
                        details=f"设备连接测试成功: {message}"
                    )
                    return jsonify({'success': True, 'message': '设备连接成功且响应正常'})
                else:
                    device.status = DeviceStatus.FAULT
                    device.retry_count += 1
                    db.session.commit()

                    # 记录失败日志
                    log_device_action(
                        device_id=device_id,
                        action='test_connection',
                        result='failed',
                        user_id=session.get('user_id'),
                        details=f"设备连接但无响应: {message}"
                    )

                    return jsonify({'success': False, 'message': f'设备连接但无响应: {message}'})
            except Exception as e:
                device.status = DeviceStatus.FAULT
                device.retry_count += 1
                db.session.commit()
                # 记录异常日志
                log_device_action(
                    device_id=device_id,
                    action='test_connection',
                    result='error',
                    user_id=session.get('user_id'),
                    details=f"设备连接但验证失败: {str(e)}"
                )

                return jsonify({'success': False, 'message': f'设备连接但验证失败: {str(e)}'})
        else:
            # 更新设备状态
            device.status = DeviceStatus.OFFLINE
            device.retry_count += 1
            db.session.commit()

            # 记录连接失败日志
            log_device_action(
                device_id=device_id,
                action='test_connection',
                result='failed',
                user_id=session.get('user_id'),
                details="设备连接失败"
            )

            return jsonify({'success': False, 'message': '设备连接失败'})

    except Exception as e:
        device.status = DeviceStatus.OFFLINE
        device.retry_count += 1
        db.session.commit()

        # 记录异常日志
        log_device_action(
            device_id=device_id,
            action='test_connection',
            result='error',
            user_id=session.get('user_id'),
            details=f"连接测试异常: {str(e)}"
        )

        return jsonify({'success': False, 'message': f'连接测试失败: {str(e)}'})


# 后台管理 - 远程开门
@zktime_bp.route('/device/<int:device_id>/remote_open', methods=['POST'])
@login_required
def remote_open(device_id):
    """远程开门操作"""
    device = ZktimeDevice.get_by_id(device_id)
    if not device:
        return jsonify({'success': False, 'message': '设备不存在'})

    # 验证门ID参数
    try:
        door_id = int(request.form.get('door_id', 1))
        if not 1 <= door_id <= 4:  # 假设最多4个门
            raise ValueError("无效的门ID")
    except ValueError as e:
        # 记录参数错误日志
        log_device_action(
            device_id=device_id,
            action='remote_open',
            result='failed',
            user_id=session.get('user_id'),
            details=f"参数错误: {str(e)}"
        )
        return jsonify({'success': False, 'message': f'无效的门ID: {str(e)}'})

    try:
        zk_device = ZKDevice(
            device.ip_address,
            device.port,
            device.protocol,
            device.password
        )

        # 使用上下文管理器确保连接关闭
        with zk_device:
            if not zk_device.connect():
                # 记录连接失败日志
                log_device_action(
                    device_id=device_id,
                    action='remote_open',
                    result='failed',
                    user_id=session.get('user_id'),
                    details=f"门{door_id}开门失败: 设备连接失败"
                )

                return jsonify({'success': False, 'message': '设备连接失败'})

            # 执行远程开门操作
            result = zk_device.open_door(door_id)

        if result:
            # 记录开门成功日志
            log_device_action(
                device_id=device_id,
                action='remote_open',
                result='success',
                user_id=session.get('user_id'),
                details=f"门{door_id}开门成功"
            )
            return jsonify({'success': True, 'message': f'门 {door_id} 开门成功'})
        else:
            # 记录开门失败日志
            log_device_action(
                device_id=device_id,
                action='remote_open',
                result='failed',
                user_id=session.get('user_id'),
                details=f"门{door_id}开门失败: 设备返回失败"
            )

            return jsonify({'success': False, 'message': '开门失败，请检查设备状态'})

    except Exception as e:
        error_msg = f'开门操作失败: {str(e)}'
        # 记录开门异常日志
        log_device_action(
            device_id=device_id,
            action='remote_open',
            result='error',
            user_id=session.get('user_id'),
            details=f"门{door_id}开门异常: {str(e)}"
        )
        return jsonify({'success': False, 'message': error_msg})


# 后台管理 - 远程重启设备
@zktime_bp.route('/device/<int:device_id>/restart', methods=['POST'])
@login_required
def remote_restart(device_id):
    """远程重启设备"""
    device = ZktimeDevice.get_by_id(device_id)
    if not device:
        return jsonify({'success': False, 'message': '设备不存在'})

    try:
        zk_device = ZKDevice(
            device.ip_address,
            device.port,
            device.protocol,
            device.password
        )

        # 使用上下文管理器确保连接关闭
        with zk_device:
            if not zk_device.connect():
                # 记录连接失败日志
                log_device_action(
                    device_id=device_id,
                    action='remote_restart',
                    result='failed',
                    user_id=session.get('user_id'),
                    details="重启失败: 设备连接失败"
                )
                return jsonify({'success': False, 'message': '设备连接失败'})

            # 执行远程重启操作
            result = zk_device.restart_device()

        if result:
            # 记录重启成功日志
            log_device_action(
                device_id=device_id,
                action='remote_restart',
                result='success',
                user_id=session.get('user_id'),
                details="设备重启命令发送成功"
            )

            return jsonify({'success': True, 'message': '设备重启命令已发送'})
        else:
            # 记录重启异常日志
            log_device_action(
                device_id=device_id,
                action='remote_restart',
                result='error',
                user_id=session.get('user_id'),
                details=f"重启失败，请检查设备状态"
            )
            return jsonify({'success': False, 'message': '重启失败，请检查设备状态'})

    except Exception as e:
        # 记录重启异常日志
        log_device_action(
            device_id=device_id,
            action='remote_restart',
            result='error',
            user_id=session.get('user_id'),
            details=f"重启异常: {str(e)}"
        )
        error_msg = f'重启操作失败: {str(e)}'
        return jsonify({'success': False, 'message': error_msg})


# 添加用户列表API路由（支持分页和搜索）
@zktime_bp.route('/device/users_list')
@login_required
def users_list():
    """获取用户列表（分页+搜索）"""
    try:
        page = request.args.get('page', 1, type=int)
        per_page = request.args.get('per_page', 10, type=int)
        keyword = request.args.get('keyword', '').strip()

        # 构建查询
        query = AdminMember.query

        if keyword:
            # 搜索姓名、工号、卡号
            search_filter = or_(
                AdminMember.name.ilike(f'%{keyword}%')
            )
            query = query.filter(search_filter)

        # 按ID排序
        query = query.order_by(AdminMember.id)

        # 分页
        pagination = query.paginate(
            page=page,
            per_page=per_page,
            error_out=False
        )

        users_data = []
        for user in pagination.items:
            user_data = {
                'id': user.id,
                'name': user.name,
            }

            # 添加可选字段
            if hasattr(user, 'employee_number'):
                user_data['employee_number'] = user.employee_number
            else:
                user_data['employee_number'] = None

            if hasattr(user, 'card_number'):
                user_data['card_number'] = user.card_number
            else:
                user_data['card_number'] = None

            # 部门信息 - 根据你的实际模型结构调整
            if hasattr(user, 'department'):
                if hasattr(user.department, 'name'):
                    user_data['department'] = user.department.name
                else:
                    user_data['department'] = str(user.department)
            else:
                user_data['department'] = None

            users_data.append(user_data)

        return jsonify({
            'users': users_data,
            'pagination': {
                'page': page,
                'per_page': per_page,
                'total': pagination.total,
                'pages': pagination.pages
            }
        })

    except Exception as e:
        import traceback
        print(f"获取用户列表错误: {str(e)}")
        print(traceback.format_exc())
        return jsonify({
            'users': [],
            'pagination': {
                'page': 1,
                'per_page': per_page,
                'total': 0,
                'pages': 0
            },
            'error': str(e)
        }), 500


# 后台管理 - 同步用户到设备
@zktime_bp.route('/device/<int:device_id>/sync_users', methods=['POST'])
@login_required
def sync_users_to_device(device_id):
    """同步用户到设备"""
    device = ZktimeDevice.get_by_id(device_id)
    if not device:
        return jsonify({'success': False, 'message': '设备不存在'})

    # 获取要同步的用户ID列表
    user_ids = request.form.getlist('user_ids[]')
    if not user_ids:
        return jsonify({'success': False, 'message': '请选择要同步的用户'})

    try:
        zk_device = ZKDevice(
            device.ip_address,
            device.port,
            device.protocol,
            device.password
        )

        with zk_device:
            if not zk_device.connect():
                # 记录连接失败日志
                log_device_action(
                    device_id=device_id,
                    action='sync_users',
                    result='failed',
                    user_id=session.get('user_id'),
                    details="用户同步失败: 设备连接失败"
                )
                return jsonify({'success': False, 'message': '设备连接失败'})

            success_count = 0
            failed_users = []

            for user_id in user_ids:
                user = AdminMember.query.get(user_id)
                if not user:
                    failed_users.append(f"用户ID {user_id} 不存在")
                    continue

                # 上传用户到设备
                result = zk_device.set_user(
                    user_id=user.id,
                    name=user.name,
                    card_number=getattr(user, 'card_number', None),
                    privilege=0  # 普通用户权限
                )

                if result:
                    success_count += 1
                else:
                    failed_users.append(user.name)

            message = f'用户同步完成，成功: {success_count}, 失败: {len(failed_users)}'
            if failed_users:
                message += f'\n失败用户: {", ".join(failed_users[:5])}'  # 只显示前5个失败用户

            # 记录同步日志
            log_device_action(
                device_id=device_id,
                action='sync_users',
                result='success' if success_count > 0 else 'failed',
                user_id=session.get('user_id'),
                details=f"同步用户: 成功{success_count}个, 失败{len(failed_users)}个"
            )

            return jsonify({
                'success': success_count > 0,
                'message': message
            })

    except Exception as e:
        error_msg = f'同步失败: {str(e)}'

        # 记录同步异常日志
        log_device_action(
            device_id=device_id,
            action='sync_users',
            result='error',
            user_id=session.get('user_id'),
            details=f"用户同步异常: {str(e)}"
        )

        return jsonify({'success': False, 'message': error_msg})


# 后台管理 - 设备状态监控
@zktime_bp.route('/device/status', methods=['GET'])
@login_required
def device_status():
    devices = ZktimeDevice.query.all()
    status_report = []

    for device in devices:
        status = {
            'id': device.id,
            'name': device.name,
            'status': device.status,
            'last_heartbeat': device.last_heartbeat.isoformat() if device.last_heartbeat else None,
            'retry_count': device.retry_count
        }
        status_report.append(status)

    return jsonify({'success': True, 'data': status_report})


# 后台管理 - 开门日志
@zktime_bp.route('/logs', methods=['GET'])
@login_required
def access_logs():
    """获取访问日志 - 支持筛选功能"""
    try:
        # 获取筛选参数
        action_type = request.args.get('action_type')
        result = request.args.get('result')
        start_date = request.args.get('start_date')
        end_date = request.args.get('end_date')

        # 使用模型方法获取带详细信息的日志
        logs = ZktimeAccessLog.get_all_with_details()

        # 应用筛选条件
        if action_type:
            logs = [log for log in logs if log['log'].action == action_type]
        if result:
            logs = [log for log in logs if log['log'].result == result]
        if start_date:
            start_date = datetime.strptime(start_date, '%Y-%m-%d')
            logs = [log for log in logs if log['log'].created_at >= start_date]
        if end_date:
            end_date = datetime.strptime(end_date, '%Y-%m-%d') + timedelta(days=1)
            logs = [log for log in logs if log['log'].created_at < end_date]

        # 转换为模板需要的格式
        formatted_logs = []
        for log in logs:
            formatted_logs.append({
                'created_at': log['log'].created_at,
                'user_name': log.get('user_name'),
                'device_name': log.get('device_name'),
                'action': log['log'].action,
                'result': log['log'].result,
                'ip_address': log['log'].ip_address,
                'details': log['log'].details
            })

        return render_template('admin/zktime/zk_logs.html', logs=formatted_logs)

    except Exception as e:
        current_app.logger.error(f"获取日志数据失败: {str(e)}")
        flash('获取日志数据失败', 'error')
        return render_template('admin/zktime/zk_logs.html', logs=[])


# 启动定时任务
def start_scheduler():
    scheduler = BackgroundScheduler()
    scheduler.add_job(
        func=check_device_status,
        trigger='interval',
        minutes=5,
        id='device_status_check'
    )
    scheduler.start()


def log_device_action(device_id, action, result, user_id=None, ip_address=None, details=None):
    """统一的设备操作日志记录函数"""
    try:
        log = ZktimeAccessLog(
            user_id=user_id,
            device_id=device_id,
            action=action,
            result=result,
            ip_address=ip_address or request.remote_addr,
            details=details  # 添加details字段
        )
        db.session.add(log)
        db.session.commit()

        if details:
            current_app.logger.info(f"设备操作日志 - 设备ID:{device_id}, 操作:{action}, 结果:{result}, 详情:{details}")
        else:
            current_app.logger.info(f"设备操作日志 - 设备ID:{device_id}, 操作:{action}, 结果:{result}")

    except Exception as e:
        current_app.logger.error(f"记录设备操作日志失败: {str(e)}")
        db.session.rollback()